package fn;

import java.util.*;

class Parser {

	private HashMap<Character, Integer> hm;

	Parser() {
		hm = new HashMap<Character, Integer>();
		hm.put('*', 1);
		hm.put('+', 2);
		hm.put('-', 3);
	}

	private Function simplize2(String s) {
		boolean found = false;
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '*') {
				found = true;
				return Multiply.create(derivative(s.substring(0, i)), parse(s.substring(i + 1, s.length())));
			}
			if (s.charAt(i) == '/') {
				found = true;
				return Divide.create(derivative(s.substring(0, i)), parse(s.substring(i + 1, s.length())));
			}
			int count = 0;
			if (s.charAt(i) == '(') {
				count++;
				i++;
				while (count != 0) {
					if (s.charAt(i) == ')')
						count--;
					else if (s.charAt(i) == '(')
						count++;
					i++;
				}
				i--;
			}
		}
		if (!found)
			if (s.charAt(0) == '(' && s.charAt(s.length() - 1) == ')')
				return derivative(s.substring(1, s.length() - 1));
			else
				return derivative(s);
		return null;
	}

	Function parse(String s) {
		boolean found = false;
		for (int i = 0; i < s.length(); i++) {
			// System.out.println(s.charAt(i));
			if (s.charAt(i) == '+') {
				found = true;
				return Add.create(derivative(s.substring(0, i)), parse(s.substring(i + 1, s.length())));
			} else if (s.charAt(i) == '-') {
				found = true;
				if (i != 0)
					return Subtract.create(derivative(s.substring(0, i)), parse(s.substring(i + 1, s.length())));
				else
					return Multiply.create(Constant.create(-1), parse(s.substring(i + 1, s.length())));
			}
			int count = 0;
			if (s.charAt(i) == '(') {
				count++;
				i++;
				while (count != 0) {
					if (s.charAt(i) == ')')
						count--;
					else if (s.charAt(i) == '(')
						count++;
					i++;
				}
				i--;
			}
		}
		if (!found)
			return derivative(s);
		return null;
	}

	private Function derivative(String a) {
		if ((a.contains("*") || a.contains("/")) && (getMiddle(a) == '/' || getMiddle(a) == '*'))
			return simplize2(a);
		if (a.charAt(0) == '(' && a.charAt(a.length() - 1) == ')') {
			return parse(a.substring(1, a.length() - 1));
		}
		if (getMiddle(a) != '^') {
			double cons[] = getConstant(a);
			if (cons[0] != 0) {

				return Multiply.create(Constant.create(cons[1]), parse(a.substring((int) cons[0], a.length())));
			}
		}
		if (a.length() > 3 && a.substring(0, 3).equals("cos") && !checkPower(a.substring(3 + 1, a.length()))) {
			return Cosine.create(parse(a.substring(3 + 1, a.length() - 1)));
		}
		if (a.length() > 3 && a.substring(0, 3).equals("sin") && !checkPower(a.substring(3 + 1, a.length()))) {
			return Sine.create(parse(a.substring(3 + 1, a.length() - 1)));
		}
		if (a.length() > 2 && a.substring(0, 2).equals("ln") && !checkPower(a.substring(2 + 1, a.length()))) {
			return Ln.create(parse(a.substring(2 + 1, a.length() - 1)));
		}
		if (a.length() == 1)
			if (a.charAt(0) == 'x')
				return Variable.create();
			else if (a.charAt(0) == 'e')
				return Constant.E;
			else
				return Constant.create(Double.parseDouble(a));
		String s[] = getPower(a);
		if (s != null && getMiddle(a) == '^') {
			return Power.create(parse(s[0]), parse(s[1]));
		}
		return Constant.create(Double.parseDouble(a));
	}

	private boolean checkPower(String substring) {
		boolean found = false;
		for (int i = substring.length() - 1; i >= 0 && !found; i--) {
			if (substring.charAt(i) == '^')
				found = true;
			if (substring.charAt(i) == ')')
				break;
		}
		return found;
	}

	private double[] getConstant(String a) {
		int in = a.indexOf("x");
		int in2 = a.indexOf("c");
		int in3 = a.indexOf("s");
		int n4 = a.indexOf("e");
		int n5 = a.indexOf("l");
		int aa[] = {
				in, in2, in3, n4, n5
		};
		double ar[] = new double[2];
		ar[0] = getMin(aa);
		if (ar[0] != 0)
			ar[1] = Double.parseDouble(a.substring(0, (int) (ar[0])));
		return ar;
	}

	private char getMiddle(String s) {
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '*' || s.charAt(i) == '/' || s.charAt(i) == '^')
				return s.charAt(i);
			int count = 0;
			if (s.charAt(i) == '(') {
				count++;
				i++;
				while (count != 0) {
					if (s.charAt(i) == ')')
						count--;
					else if (s.charAt(i) == '(')
						count++;
					i++;
				}
				i--;
			}
		}
		return 'f';
	}

	private double getMin(int a[]) {
		double min = 1 << 25;
		double x = 1 << 25;
		for (int i = 0; i < a.length; i++) {
			if (a[i] != -1)
				min = Math.min(min, a[i]);
		}
		return min != x ? min : 0;
	}

	private String[] getPower(String s) {
		for (int i = 0; i < s.length(); i++) {
			if (s.charAt(i) == '*' || s.charAt(i) == '/' || s.charAt(i) == '^')
				return new String[] {
						s.substring(0, i), s.substring(i + 1, s.length())
				};
			int count = 0;
			if (s.charAt(i) == '(') {
				count++;
				i++;
				while (count != 0) {
					if (s.charAt(i) == ')')
						count--;
					else if (s.charAt(i) == '(')
						count++;
					i++;
				}
				i--;
			}
		}
		return null;
	}

}
